/*********************************************************************************
 *       Filename:  thread_main.c
 *    Description:  
 *
 *        Version:  1.0.0(2024/12/09 22:14:30)
 *         Author:  Zhang Long <1318085470@qq.com>
 *      ChangeLog:  1, Release initial version on "2024/12/09 22:14:30"
 ********************************************************************************/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <sqlite3.h>
#include <sys/epoll.h>
#include <poll.h>
#include <pthread.h>
#include <sys/select.h> 
#include <pthread.h>
#include <ctype.h>
#include "database.h"
#include "ds18b20.h"
#include "transferdata.h"
#include "localtime.h"
#include "socket.h"
#include "logger.h"
#include "proc.h"

#define MAX_EVENTS 512
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))

typedef void *(THREAD_BODY) (void *thread_arg);
void *thread_worker(void *ctx);
int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg);
static inline void print_usage(char *progname)
{
    printf("%s usage: \n", progname);
    printf("-i(--ipaddr): specify server IP address(also not specify, default is all)\n");
    printf("-p(--port):   specify server PORT\n");
	printf("-d(--debug):  specify program running in debug mode\n");
    printf("-h(--help):   print this help information\n");

	printf("\nExample: %s -p 12345 -d\n", progname);

    return ;
}
    sqlite3 		    *db;
	char			    *db_file = "server.db";
	char			    *log_file = "server.log";
	int					log_level = LOG_LEVEL_INFO;


int main(int argc,char** argv)
{
	//int					log_level = LOG_LEVEL_INFO;

    int                 pack_bytes = 0;
   //  char			    *log_file = "server.log";
    char                *progname = NULL;
    int                 ch;
    char                *listen_ip = NULL;
	int			        listen_port = 0;   
    int                 daemon_run = 1; 
    long				log_size = 1000*1024;
   // sqlite3 		    *db;
	//char			    *db_file = "server.db";
    socket_t			sock = {0};
	int                 epoll_fd;
	struct epoll_event  event;
	struct epoll_event  event_array[MAX_EVENTS];
	int					events = 0;
	int 				i;
	int					connect_fd = -1;
    int                 max;
	struct sockaddr_in  client_addr;
	socklen_t			client_addr_len = sizeof(client_addr);
	int					rv = -1;
	char				pack_buf[1024] = {0};
//	packet_t			pack = {0};
    struct              pollfd fds_array[1024];
    int                 found;
	char 				buf[1024];
	int					on =1;   
    pid_t               pid;
	pthread_t 			tid;
    struct option opts[]=
    {
        {"ipaddr", required_argument, NULL, 'i'},
        {"port", required_argument, NULL, 'p'},
        {"debug", no_argument, NULL, 'd'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };


    progname = basename(argv[0]);
    while ( -1 != (ch=getopt_long(argc, argv, "i:p:dh", opts, NULL)) )
	{
		switch (ch)
		{
			case 'i':
				listen_ip = optarg;
				break;

			case 'p':
				listen_port = atoi(optarg);
				break;

			case 'd':
				daemon_run = 0;
				log_file = "console";
				log_level = LOG_LEVEL_DEBUG;			
				break;

			case 'h':
				print_usage(progname);
				return 0;

			default:
				return -1;
		}
	}
    if( log_open(log_file, log_level, log_size, THREAD_LOCK_NONE) < 0 )
    {
        fprintf(stderr, "Initial log system failed\n");
        return 1;
    }
  	log_debug("log system open ok\n");
    if (!listen_port)
	{
		print_usage(progname);
		return -3;
	}
	log_debug("Parser arguments ok: IP[%s], PORT[%d], LOG[%s]\n", listen_ip, listen_port, log_file);

    set_socket_rlimit();
    install_signal();

    db_open(&db, db_file);

	db_create(db, TA_CREATE);
    
	if (daemon_run)
	{
		if (daemon(0, 0) < 0)
		{
			log_error("daemon() error: %s\n", strerror(errno));
			return -4;
		}
		else
		{
			log_info("Program running in daemon mode\n");
            log_info("thread mode\n");
		}
	}
    socket_init(&sock, listen_ip, listen_port);
    if (socket_server_init(&sock) < 0)
	{
		log_error("socket_listen() error\n");
		return -5;
	}
    setsockopt(sock.fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	log_debug("Socket listen ok\n");
    while(1)
    {
        log_info("start accept new client incoming..\n");
        if ( (connect_fd=accept(sock.fd, (struct sockaddr *)&client_addr, &client_addr_len)) < 0 )
        {
            log_info("accept new client failure: %s\n", strerror(errno));
            continue;
        }
        log_info("Accept new client[%s:%d] with fd [%d]\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), connect_fd);  
		thread_start(&tid, thread_worker, (void *)connect_fd);
    }
	socket_close(&sock);
	db_close(db, log_file);
	//close(sock.fd);
	log_close();
    return 0;
}

int thread_start(pthread_t * thread_id, THREAD_BODY * thread_workbody, void *thread_arg)
{
 	int rv = -1;
 	pthread_attr_t thread_attr;
 	if( pthread_attr_init(&thread_attr) )
 	{
 		log_info("pthread_attr_init() failure: %s\n", strerror(errno));
 		goto CleanUp;
 	}
 	if( pthread_attr_setstacksize(&thread_attr, 120*1024) )
 	{
 		log_info("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
 		goto CleanUp;
 	}
 	if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )
 	{
		log_info("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
 		goto CleanUp;
 	}
 /* Create the thread */
 	if( pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg) )
 	{
 		log_info("Create thread failure: %s\n", strerror(errno));
 		goto CleanUp;
 	}
 	rv = 0;
 
CleanUp:
 
 /* Destroy the attributes of thread */
 pthread_attr_destroy(&thread_attr);
 return rv;
}

void *thread_worker(void *ctx)
{
	int clifd;
	char buf[1024];
	int rv;
	int i;
	packet_t			pack = {0};
	if( !ctx )
 	{
 		log_info("Invalid input arguments in %s()\n", __FUNCTION__);
 		pthread_exit(NULL);
 	}
 	clifd = (int)ctx;

	log_info("Child thread start to commuicate with socket client...\n");
	while(1)
	{
		memset(buf, 0, sizeof(buf));
        if( (rv=socket_read(clifd , buf, sizeof(buf))) <= 0)
        {
            log_info("socket[%d] read failure or get disconncet.\n", clifd);
            close(clifd);
            pthread_exit(NULL);
        }
        else
        {
            log_info("Read %d bytes data[%s] from socket[%d] ok\n", rv, buf, clifd);
            packet_segmented_unpack(&pack, buf);		
			write_to_db(db, &pack);		
        }
	}
}